/*!
 * Name: Weapp-Storage
 * Version: v1.0.0
 * Description: 微信小程序数据缓存API的封装
 * Author: Liu Jiachang
 *
 * 接口详情
 * https://developers.weixin.qq.com/miniprogram/dev/api/storage/wx.setStorageSync.html
 * 
 * 存储能力
 * https://developers.weixin.qq.com/miniprogram/dev/framework/ability/storage.html
 */

const name = 'Weapp-Storage',
    KEY_NOT_FOUND = `[${name}] key不存在！`,
    MAX_ARRAY_SIZE = 200

const toString = Object.prototype.toString

const noop = function() {}
const methodsToPatch = [
  'getStorageSync',
  'setStorageSync',
  'removeStorageSync',
  'clearStorageSync',
  'getStorageInfoSync',
]

let _wx
// 使用纯净的_wx对象
if (typeof wx === 'object' && wx !== null && wx.__proto__.isPrototypeOf(Object) && wx) {
  _wx = Object.create(null)
  let index = -1
  while (++index < methodsToPatch.length) {
    _wx[methodsToPatch[index]] = wx[methodsToPatch[index]]
                                  ? wx[methodsToPatch[index]]
                                  : noop 
  }
} else {
  throw `[${name}] 仅支持在微信小程序环境中运行！`
}

function wxGetStorage(key) {
  try {
    const value = _wx.getStorageSync(key)
    return value || ''
  } catch (e) {}
}

function wxSetStorage(key, value) {
  try {
    const { currentSize, limitSize } = _wx.getStorageInfoSync()
    const flag = currentSize < limitSize
    if (flag) {
      _wx.setStorageSync(key, value)
    }
    return { storage: flag, currentSize, limitSize }
  } catch (e) {}
}

function wxRemoveStorage(key) {
  try {
    _wx.removeStorageSync(key)
    return true
  } catch (e) {}
}

function wxClearStorage() {
  try {
    _wx.clearStorageSync()
  } catch(e) {}
}

function logger(title) {
  console.log(new Date().toLocaleTimeString() + '：' + title)
}

/**
 * 获取数组中最后一个元素
 *
 * @param {Array} array
 * @returns 数组最后一个元素
 */
function last(array) {
  return array && array[array.length - 1]
}

/**
 * 是否为字符串类型
 *
 * @param {*} value 
 * @returns 返回true或者false
 */
function isString(value) {
  return value != null && toString.call(value) === '[object String]'
}

/**
 * base getStorage
 *
 * @param {string[]} keys key数组
 * @param {Function} iteratee 迭代函数
 * @param {Function} comparator 比较函数
 * @returns 返回每个key对应的结果
 */
function baseGet(keys, iteratee, comparator) {
  if (keys === undefined) {
    throw KEY_NOT_FOUND
  }
  if (!!iteratee && typeof iteratee !== 'function') {
    throw new TypeError(`[${name}] baseGet iteratee参数必须为函数！`)
  }

  const { length } = keys
  let result = new Array(length)

  // 对keys内的数据进行校验
  if (typeof comparator !== 'function') {
    comparator = undefined
  }
  if (comparator) {
    let index = -1
    while (++index < length) {
      if (!comparator(keys[index])) {
        throw new TypeError(`[${name}] baseGet key必须为字符串！`)
      }
    }
  }

  let keyIndex = -1
  while (++keyIndex < length) {
    const storage = wxGetStorage(keys[keyIndex])
    const value = !!iteratee
                    ? iteratee(storage)
                    : storage
    result[keyIndex] = value
  }

  return result.length > 1 ? result : result[0]
}

/**
 * base setStorage
 *
 * @param {string[]} keys key数组
 * @param {any[]} values value数组
 * @param {Function} iteratee 迭代函数
 * @param {Function} comparator 比较函数
 * @returns 缓存是否写入成功
 */
function baseSet(keys, values, iteratee, comparator) {
  if (keys === undefined) {
    throw KEY_NOT_FOUND
  }
  if (!!iteratee && typeof iteratee !== 'function') {
    throw new TypeError(`[${name}] baseGet iteratee参数必须为函数！`)
  }

  keys = Array.isArray(keys) ? keys : [keys]
  values = Array.isArray(values) ? values : [values]
  const { length } = keys
  let result = new Array(length)

  // 对keys内的数据进行校验
  if (typeof comparator !== 'function') {
    comparator = undefined
  }
  if (comparator) {
    let index = -1
    while (++index < length) {
      if (!comparator(keys[index])) {
        throw new TypeError(`[${name}] baseSet key必须为字符串！`)
      }
    }
  }

  const vLen = values.length
  if (vLen !== length) {
    if (vLen > length) {
      values = values.slice(0, length)
    }
    if (vLen < length) {
      values = [...values, ...Array.from({ length: length - vLen }, () => '')]
    }
  }

  let keyIndex = -1
  while (++keyIndex < length) {
    const key = keys[keyIndex]
    const tempVal = values[keyIndex]
    const value = !!iteratee
                    ? iteratee(tempVal)
                    : tempVal
    const { storage, currentSize, limitSize } = wxSetStorage(key, value)
    if (storage) {
      result[keyIndex] = storage
      logger(`缓存[${key}]写入成功：${value}，当前占用的空间大小：${currentSize}KB，限制的空间大小：${limitSize}KB`)
    }
  }

  return result.length > 1 ? result : result[0]
}

/**
 * base removeStorage
 *
 * @param {*} keys key数组
 * @param {*} comparator 比较函数
 * @returns 缓存是否移除成功
 */
function baseRemove(keys, comparator) {
  if (keys === undefined) {
    throw KEY_NOT_FOUND
  }

  keys = Array.isArray(keys) ? keys : [keys]
  const { length } = keys
  let result = new Array(length)

  // 对keys内的数据进行校验
  if (typeof comparator !== 'function') {
    comparator = undefined
  }
  if (comparator) {
    let index = -1
    while (++index < length) {
      if (!comparator(keys[index])) {
        throw new TypeError(`[${name}] baseRemove key必须为字符串！`)
      }
    }
  }

  let keyIndex = -1
  while (++keyIndex < length) {
    const key = keys[keyIndex]
    const storage = wxRemoveStorage(key)
    if (storage) {
      result[keyIndex] = storage
    }
  }

  return result.length > 1 ? result : result[0]
}

const localStorage = {
  /**
   * 从本地缓存中获取指定key的内容
   *
   * @param {string} key
   * @param {...(string|Function|string[])} [args] 最后一个参数传递函数可对返回结果进行一次操作
   * @returns {string|Array} 返回一个值或多个值
   * @example
   * 
   * localStorage.getItem('key1')
   * // => value1
   * 
   * 返回结果的每个值都会调用一次Math.floor
   * localStorage.getItem('key1', 'key2', 'key3', Math.floor)
   * // => [value1, value2, value3]
   */
  getItem: (key, ...args) => {
    let keys = [], iteratee = undefined
    keys = Array.from(args)
    keys.unshift(key)
    if (typeof last(args) === 'function') {
      iteratee = last(args)
      keys.pop()
    }
    return baseGet(keys, iteratee, isString)
  },
  /**
   * 将数据存储在本地缓存中指定的key中
   *
   * @param {string|string[]} keys
   * @param {any|any[]} values
   * @param {Function} iteratee
   * @example
   * 
   * localStorage.setItem('key1', 'value1')
   *
   * localStorage.setItem(['key1', 'key2'], ['value1', 'value2'], value => value + 'a')
   */
  setItem: (keys, values, iteratee = null) => {
    return baseSet(keys, values, iteratee, isString)
  },
  /**
   * 从本地缓存中移除指定key
   *
   * @param {string|string[]} keys
   * @example
   * 
   * localStorage.removeItem('key1')
   *
   * localStorage.removeItem(['key1', 'key2'])
   */
  removeItem: (keys) => {
    return baseRemove(keys, isString)
  },
  /**
   * 清理本地数据缓存
   * 
   * @example
   * 
   * localStorage.clear()
   */
  clear: () => {
    wxClearStorage()
  },
}

module.exports = localStorage